/**
 * @format
 * @Author: TC
 * @Date: 2023-03-18 18:05:53
 * @Last Modified by: TC
 * @Last Modified time: 2025-05-04 21:22:51
 */

import { BlockInputEvents, isValid, Label, Node, size, UITransform, view } from "cc";
import { app } from "../App";
import { ITip, IUIData } from "../Common/BaseInterface";
import { EUILayer } from "../Const/ComponentConst";
import { EFrameUIConst, FrameUIConfig } from "../Const/FrameUIConst";
import { BaseUI } from "../Module/Component/BaseUI";
import { IUIConfig } from "../Module/UI/UIInterface";
import { EUIStatus, UIUnit } from "../Module/UI/UIUnit";

/**
 * UI管理类
 */
export class UIManager {
    private _windowUuid = 0;
    //窗口配置列表
    private _mapCfgList: Map<string, IUIConfig> = new Map();
    // 窗口列表
    private _mapUI: Map<number, UIUnit> = new Map();
    //加载节点
    private _loadingNode: Node = null;
    constructor() {}

    init(): void {
        this.addUICfg(FrameUIConfig);
    }

    /**
     * 打开窗口
     * @param uiName 窗口Key
     * @param data 每次打开传入的窗口，最好继承IUIData，避免any类型
     * @param cb 打开窗口后的回调
     * @returns
     */
    public openUI(uiName: string, data?: IUIData, cb?: (comp: BaseUI) => void): void {
        const uiCfg = this._createUICfg(uiName);
        const hidedUI = this._getHidedUI(uiName);
        if (hidedUI) {
            hidedUI.show(data);
            if (cb) cb(hidedUI.getUIComp());
            return;
        }
        const existedUI = this._getExistedUI(uiName);
        if (existedUI) {
            if (uiCfg.canOpenMore) {
                this._createUI(uiName, data, cb);
            } else {
                app.log.warn("open existed ui", uiName);
            }
            return;
        }
        this._createUI(uiName, data, cb);
    }

    /**
     * 尝试显示已存在并隐藏的窗口
     * @param uiName
     * @returns
     */
    private _getExistedUI(uiName: string): UIUnit {
        let uiUint: UIUnit = null;
        this._mapUI.forEach((ui) => {
            if (ui.uiCfg.uiName === uiName) {
                uiUint = ui;
            }
        });
        return uiUint;
    }

    /**
     * 获取隐藏的窗口
     * @param uiName
     * @returns
     */
    private _getHidedUI(uiName: string): UIUnit {
        let uiUint: UIUnit = null;
        this._mapUI.forEach((ui) => {
            if (ui.uiCfg.uiName === uiName && ui.isClosed()) {
                uiUint = ui;
            }
        });
        return uiUint;
    }
    /**
     * 创建窗口
     * @param uiName
     * @param data
     * @param uuid
     */
    private _createUI(uiName: string, data?: IUIData, cb?: (comp: BaseUI) => void): void {
        const uiCfg = this._createUICfg(uiName);
        uiCfg.uiName = uiName;
        if (uiCfg.closeOther) {
            this.closeAllUIs();
        }
        this._windowUuid++;
        const ui = new UIUnit(uiCfg, this._windowUuid);
        this._mapUI.set(ui.uuid, ui);
        ui.open(data, (comp) => {
            if (cb) cb(comp);
        });
    }

    /**
     * 通过uid关闭窗口
     * @param uiName
     * @param uid 支持打开多个相同窗口时，传入窗口id
     * @returns
     */
    public closeUI(uiName: string, uid?: number): void {
        this._mapUI.forEach((ui) => {
            if (ui.uiCfg.uiName === uiName) {
                if (!uid || uid === ui.uuid) {
                    this._tryCloseUI(ui);
                }
            }
        });
    }

    /**
     * 通过uid销毁窗口
     * @param uiName
     * @param uid 支持打开多个相同窗口时，传入窗口id
     * @returns
     */
    public destroyUI(uiName: string, uid?: number): void {
        this._mapUI.forEach((ui) => {
            if (ui.uiCfg.uiName === uiName) {
                if (!uid || uid === ui.uuid) {
                    ui.destruoy();
                    this._mapUI.delete(ui.uuid);
                }
            }
        });
    }

    /**
     * 尝试关闭窗口
     * @param ui
     */
    private _tryCloseUI(ui: UIUnit): void {
        ui.close(false);
        if (ui.uiCfg.autoDestroy) {
            this._mapUI.delete(ui.uuid);
        }
    }

    /**
     * 打开上一个全屏窗口
     */
    public openPreFullUI(): void {
        const fullUIList: UIUnit[] = [];
        this._mapUI.forEach((ui) => {
            if (ui.uiCfg.fullScreen && ui.status === EUIStatus.CLOSED_PASSIVE) {
                fullUIList.push(ui);
            }
        });
        if (fullUIList.length) {
            fullUIList.sort((a, b) => {
                return b.getUINode().getSiblingIndex() - a.getUINode().getSiblingIndex();
            });
            fullUIList[0].show();
        }
    }

    /**
     * 关闭指定名称之外的所有窗口
     * @param uiNames
     */
    public closeUIsWithout(uiNames: string[]): void {
        uiNames.forEach((uiName) => {
            this._mapUI.forEach((item) => {
                if (item.uiCfg.uiName === uiName) {
                    this.closeUI(uiName);
                }
            });
        });
    }

    /**
     * 关闭所有已打开的窗口
     */
    public closeAllUIs(): void {
        this._mapUI.forEach((ui) => {
            ui.close(false);
        });
    }

    /**
     * 关闭所有非全屏窗口
     */
    public closeAllNormalUIs(): void {
        this._mapUI.forEach((ui) => {
            if (!ui.uiCfg.fullScreen) {
                ui.close(false);
            }
        });
    }

    /**
     * 关闭所有全屏窗口
     */
    public closeAllFullUIs(uiName: string): void {
        this._mapUI.forEach((ui) => {
            if (ui.uiCfg.uiName != uiName) {
                if (ui.uiCfg.fullScreen && !ui.isClosed()) {
                    ui.close(ui.uiCfg.fullScreen);
                }
            }
        });
    }

    /**
     * 销毁所有窗口
     */
    public destroyAllWindows(): void {
        this._mapUI.forEach((ui) => {
            ui.destruoy();
        });
        this._mapUI.clear();
        this._windowUuid = 0;
    }

    /**
     * 根据id获取窗口单元
     * @param id
     */
    private _getUIUnitById(id: number): UIUnit {
        const uWin: UIUnit = this._mapUI.get(id);
        if (!uWin) {
            app.log.error("get WindowUnit err");
            return null;
        }
        return uWin;
    }

    /**
     * 设置加载窗口节点
     * @param node
     */
    public setLoadingNode(node: Node): void {
        this._loadingNode = node;
    }
    /**
     * 显示等待窗口
     * @param strLoading 等待文案
     */
    public showLoading(strLoading: string = ""): void {
        if (!this._loadingNode || !isValid(this._loadingNode)) {
            this._loadingNode = new Node("GlobalLoading");
            this._loadingNode.addComponent(UITransform).setContentSize(size(view.getVisibleSize()));
            this._loadingNode.addComponent(BlockInputEvents);
        }
        const parent = app.sceneMgr.getCurrScene().getLayer(EUILayer.LOADING);
        this._loadingNode.setParent(parent);
        this._loadingNode.active = true;
        const nodeLabel = this._loadingNode.getChildByName("lblContent");
        if (nodeLabel) {
            nodeLabel.getComponent(Label).string = strLoading;
        }
    }

    /**
     * 隐藏等待窗口
     */
    public hideLoading(): void {
        if (this._loadingNode) {
            this._loadingNode.active = false;
        }
    }

    /**
     * 显示提示窗口
     * @param tipStr 提示文案
     */
    public showTip(tipStr: string): void {
        const tipData: ITip = {
            name: "tip",
            tip: tipStr,
        };
        this.openUI(EFrameUIConst.TipUI, tipData);
    }
    /**
     * 初始化UI配置列表
     * @param cfgList
     */
    public addUICfg(cfgList: { [key: string]: IUIConfig }): void {
        Object.keys(cfgList).forEach((key) => {
            if (this._mapCfgList.has(key)) return;
            this._initUICfg(cfgList[key]);
            this._mapCfgList.set(key, cfgList[key]);
        });
    }

    /**
     * 移除UI配置列表
     * @param cfgList
     */
    public removeUICfg(cfgList: { [key: string]: IUIConfig }): void {
        Object.keys(cfgList).forEach((key) => {
            if (this._mapCfgList.has(key)) {
                this._mapCfgList.delete(key);
            }
        });
    }
    /**
     * 初始化UI配置
     * @param cfg
     */
    private _initUICfg(cfg: IUIConfig): void {
        cfg.layer = cfg.layer || EUILayer.UI;
    }

    private _createUICfg(key: string): IUIConfig {
        const cfg: IUIConfig = this._mapCfgList.get(key);
        return cfg;
    }
}
